package dd.lo.practice;

import dd.lo.util.ImageUtils;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Slider;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Stage;
import javafx.util.StringConverter;
import org.apache.commons.lang3.math.NumberUtils;
import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.ByteArrayInputStream;
import java.io.File;

public class FilterDisplay extends Application {

    public static void start(String[] args) {
        launch(args);
    }

    private static enum FilterEnum {
        BOX_FILTER("方框滤波（线性）"),
        MEAN_FILTER("均值滤波（线性）"),
        GAUSSIAN_FILTER("高斯滤波（线性）"),
        MEDIAN_FILTER("中值滤波（非线性）"),
        BILATERAL_FILTER("双边滤波（非线性）"),
        ;

        private final String name;

        public String getName() {
            return name;
        }

        FilterEnum(String name) {
            this.name = name;
        }
    }

    private static final int DEFAULT_WIDTH = 1200;
    private static final int DEFAULT_HEIGHT = 900;

    private Mat srcImg;

    private SimpleStringProperty k; //过滤器的核心大小

    private Slider slider;
    private FilterEnum mode;
    private ImageView imageView;

    private void imageProcess() {
        Task<Void> task = new Task<Void>() {
            @Override
            protected Void call() {
                if (null == srcImg || srcImg.empty()) return null;
                Mat displayImg = new Mat();
                int kernelSize = NumberUtils.toInt(k.getValue(), 0) + 1;
                switch (mode) {
                    case BOX_FILTER: {
                        //Point(-1, -1)代表取核的中心为锚点
                        Imgproc.boxFilter(srcImg, displayImg, -1, new Size(kernelSize, kernelSize), new Point(-1, -1), false, Core.BORDER_DEFAULT);
                        break;
                    }
                    case MEAN_FILTER: {
                        Imgproc.blur(srcImg, displayImg, new Size(kernelSize, kernelSize), new Point(-1, -1), Core.BORDER_DEFAULT);
                        break;
                    }
                    case GAUSSIAN_FILTER: {
                        if ((kernelSize & 1) == 0) kernelSize--; //高斯滤波的内核边大小必须为正奇数，sigmaX和sigmaY均为0时，由内核大小自动计算
                        Imgproc.GaussianBlur(srcImg, displayImg, new Size(kernelSize, kernelSize), 0, 0, Core.BORDER_DEFAULT);
                        break;
                    }
                    case MEDIAN_FILTER: {
                        if ((kernelSize & 1) == 0) kernelSize--; //中值滤波的内核边大小必须为正奇数
                        Imgproc.medianBlur(srcImg, displayImg, kernelSize);
                        break;
                    }
                    case BILATERAL_FILTER: {
                        Imgproc.bilateralFilter(srcImg, displayImg, -1, kernelSize * 2.0, kernelSize / 2.0, Core.BORDER_DEFAULT);
                        break;
                    }
                }
                //显示
                ImageUtils.fitImgSize(displayImg, DEFAULT_WIDTH, DEFAULT_HEIGHT);
                MatOfByte buffer = new MatOfByte();
                Imgcodecs.imencode(".jpeg", displayImg, buffer);
                Platform.runLater(() -> imageView.setImage(new Image(new ByteArrayInputStream(buffer.toArray()))));
                return null;
            }
        };
        new Thread(task).start();
    }

    @Override
    public void start(Stage stage) throws Exception {
        Button imgChooserBtn = new Button("选择图片");
        imgChooserBtn.setOnMouseClicked(e -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("选择图片");
            fileChooser.setInitialDirectory(new File(System.getProperty("user.home") + "/Downloads")
            );
            fileChooser.getExtensionFilters().addAll(new FileChooser.ExtensionFilter("All Images", "*.*"));
            File imgFile = fileChooser.showOpenDialog(stage);
            if (null == imgFile) return;
            srcImg = Imgcodecs.imread(imgFile.getAbsolutePath(), Imgcodecs.IMREAD_UNCHANGED);
            if (srcImg.empty()) System.out.println("图片读取失败");
            imageProcess();
        });
        Text filterModeTip = new Text("过滤模式：");
        ChoiceBox<FilterEnum> filterMode = new ChoiceBox<>();
        filterMode.getItems().addAll(FilterEnum.BOX_FILTER, FilterEnum.MEAN_FILTER, FilterEnum.GAUSSIAN_FILTER, FilterEnum.MEDIAN_FILTER, FilterEnum.BILATERAL_FILTER);
        filterMode.setConverter(new StringConverter<FilterEnum>() {
            @Override
            public String toString(FilterEnum object) {
                return object.getName();
            }
            @Override
            public FilterEnum fromString(String string) {
                return null;
            }
        });
        filterMode.valueProperty().addListener((observable, oldValue, newValue) -> {
            mode = newValue;
            imageProcess();
        });
        filterMode.setValue(FilterEnum.BOX_FILTER);
        Text sliderTip = new Text("内核大小：");
        k = new SimpleStringProperty();
        Text sliderValueText = new Text();
        sliderValueText.textProperty().bind(k);
        slider = new Slider(0, 50, 3);
        slider.setPrefWidth(DEFAULT_WIDTH * 0.75);
        slider.setShowTickLabels(true);
        slider.setShowTickMarks(true);
        slider.setSnapToTicks(true);
        slider.setMajorTickUnit(5.0);
        slider.setMinorTickCount(4);
        slider.setBlockIncrement(1.0);
        slider.valueProperty().addListener((observable, oldValue, newValue) -> {
            k.set(Math.round(newValue.doubleValue()) + "");
            imageProcess();
        });
        //初始化k值
        k.set(Math.round(slider.getValue()) + "");
        Button exitBtn = new Button("关闭");
        exitBtn.setOnMouseClicked(e -> Platform.exit());
        FlowPane bottomControl = new FlowPane();
        bottomControl.getChildren().addAll(imgChooserBtn, filterModeTip, filterMode, sliderTip, slider, sliderValueText, exitBtn);
        BorderPane root = new BorderPane();
        imageView = new ImageView(); //imageView放到一个pane的中间
        root.setCenter(imageView);
        root.setBottom(bottomControl);
        Scene scene = new Scene(root, DEFAULT_WIDTH, DEFAULT_HEIGHT + 60);
        stage.setScene(scene);
        stage.setTitle("各常用滤波演示");
        stage.setResizable(false);
        stage.show();
    }
}
